package cc.chenwenxi.mypi.appium;

import cc.chenwenxi.mypi.appium.kits.AppiumUtils;
import cc.chenwenxi.mypi.autoconfig.PiCnf;
import cc.chenwenxi.mypi.autoconfig.SpringConfig;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.*;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.google.common.collect.Lists;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.Activity;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.android.AndroidElement;
import io.appium.java_client.pagefactory.AndroidFindBy;
import io.appium.java_client.pagefactory.AppiumFieldDecorator;
import jodd.io.FileUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.mapdb.DB;
import org.mapdb.HTreeMap;
import org.openqa.selenium.support.PageFactory;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * com.ss.android.excitingvideo.ExcitingVideoActivity 广告页面
 * <p>
 * .activity.SplashActivity 领宝箱页面 / 小视频 / 首页 ps: 看来只有广告才不一样
 */
@Slf4j
@Data
public class 头条AppiumTask implements AppiumTask {

    public static final Activity mainActivity = new Activity("com.ss.android.article.lite", "com.ss.android.article.lite.activity.SplashActivity");
    private final AndroidDriver driver;
    private final AppiumUtils kit;
    private final DB mapDb;

    private final String pre = "头条_";

    private final HTreeMap<String, Date> hash;
    private final HTreeMap<String, ToutiaoDay> hashDay;
    private final HTreeMap.KeySet<String> toutiao_舔狗;
    private final File 舔狗日记;
    private final List<String> 日记s;

    private final List<吃饭时间> 吃饭时间s = Lists.newArrayList(
            new 吃饭时间(new DateTime("05:00"), new DateTime("09:00")),
            new 吃饭时间(new DateTime("11:00"), new DateTime("14:00")),
            new 吃饭时间(new DateTime("17:00"), new DateTime("20:00")),
            new 吃饭时间(new DateTime("21:00"), new DateTime("23:59"))
    );

    // --------------------------------导航--------------------------
    @AndroidFindBy(xpath = "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TabHost/android.widget.RelativeLayout[1]/android.widget.TabWidget/android.widget.RelativeLayout[3]/android.widget.TextView")
    private AndroidElement 导航_任务;
    @AndroidFindBy(xpath = "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TabHost/android.widget.RelativeLayout[1]/android.widget.TabWidget/android.widget.RelativeLayout[4]/android.widget.TextView")
    private AndroidElement 导航_小视频;
    @AndroidFindBy(xpath = "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TabHost/android.widget.RelativeLayout/android.widget.TabWidget/android.widget.RelativeLayout[1]/android.widget.TextView")
    private AndroidElement 导航_首页;

    // --------------------------------微头条--------------------------
    @AndroidFindBy(id = "com.ss.android.article.lite:id/b4l")
    private AndroidElement 首页_发布;
    @AndroidFindBy(uiAutomator = """
               new UiSelector().textMatches("微头条")
            """)
    private AndroidElement 首页_发布_微头条;
    @AndroidFindBy(id = "com.ss.android.newugc:id/content_et")
    private AndroidElement 首页_发布_微头条_文本框;
    @AndroidFindBy(uiAutomator = """
               new UiSelector().textMatches("发布")
            """)
    private AndroidElement 首页_发布_微头条_发布;

    // --------------------------------小视频页面--------------------------
    @AndroidFindBy(id = "com.ss.android.article.lite:id/av0")
    private AndroidElement 小视频_点赞;
    @AndroidFindBy(id = "com.ss.android.article.lite:id/ad2")
    public AndroidElement 小视频_评论;
    @AndroidFindBy(id = "com.ss.android.article.lite:id/bxp")
    public AndroidElement 小视频_评论框;
    @AndroidFindBy(id = "com.ss.android.article.lite:id/bae")
    private AndroidElement 小视频_评论界面_发布;

    // --------------------------------任务界面--------------------------
    @AndroidFindBy(uiAutomator = """
               new UiSelector().textMatches("开宝箱得金币")
            """)
    private AndroidElement 宝箱;
    @AndroidFindBy(xpath = "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TabHost/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.webkit.WebView/android.webkit.WebView/android.view.View/android.view.View[2]/android.view.View[2]/android.view.View/android.view.View/android.view.View[3]/android.view.View[1]")
    private AndroidElement 看完视频再领金币;
    //    @AndroidFindBy(uiAutomator = """
//               new UiSelector().textMatches(".*再看一个获得.*金币")
//            """)
//    public List<AndroidElement> 再看一个广告;
    @AndroidFindBy(uiAutomator = """
               new UiSelector().textMatches("关闭")
            """)
    private AndroidElement 关闭视频x按钮;
    @AndroidFindBy(uiAutomator = """
               new UiSelector().textMatches("坚持退出")
            """)
    private AndroidElement 关闭视频x按钮_第二次坚持退出;
    @AndroidFindBy(uiAutomator = """
               new UiSelector().textMatches("睡觉赚钱")
            """)
    public List<MobileElement> 睡觉赚钱;
    @AndroidFindBy(uiAutomator = """
               new UiSelector().textMatches("吃饭补贴")
            """)
    public List<MobileElement> 吃饭补贴;

    public 头条AppiumTask(AndroidDriver androidDriver) {
        this.driver = androidDriver;
        this.kit = new AppiumUtils(androidDriver);
        this.mapDb = SpringUtil.getBean(DB.class);
        this.hash = (HTreeMap<String, Date>) mapDb.hashMap("toutiao_hash_map").createOrOpen();
        this.toutiao_舔狗 = (HTreeMap.KeySet<String>) mapDb.hashSet("toutiao_tiangouriji").createOrOpen();
        this.hashDay = (HTreeMap<String, ToutiaoDay>) mapDb.hashMap("toutiao_day").createOrOpen();
        Arrays.stream(数据枚举.values()).forEach(e -> {
            Date date = hash.get(e.name());
            log.info("读取最后一次:{},Date:{}", e, DateUtil.formatDateTime(date));
            if (date == null) {
                log.info("初始化时间:{},Date:{}", e, DateUtil.formatDateTime(new Date()));
                hash.put(e.name(), new Date());
            }
        });

        final SpringConfig springConfig = SpringUtil.getBean(SpringConfig.class);
        舔狗日记 = new File(springConfig.getDataPath(), "舔狗日记.txt");

        List<String> 日记s1 = new ArrayList();
        try {
            日记s1.addAll(List.of(FileUtil.readLines(舔狗日记, StandardCharsets.UTF_8)));
        } catch (IOException e) {
            e.printStackTrace();
            日记s1 = Lists.newArrayList();
        }
        日记s = 日记s1;

        PageFactory.initElements(new AppiumFieldDecorator(driver), this);
    }

    @Override
    public void run(Date endTime) {
        while (true) {
            try {
                if (endTime.before(new Date())) {
                    log.info("运行结束!!,过期时间为:{}", DateUtil.formatDateTime(endTime));
                    break;
                }
                if (DateUtil.between(hash.get(数据枚举.上次发布微头条.name()), new Date(), DateUnit.HOUR) > RandomUtil.randomInt(3, 4)) {
                    this.发布微头条();
                } else if (DateUtil.between(hash.get(数据枚举.上次开宝箱.name()), new Date(), DateUnit.MINUTE) > 2) {
                    hash.put(数据枚举.上次开宝箱.name(), new Date());
                    String text = 导航_任务.getText();
                    log.info("{}任务文本:{}", pre, text);
                    if (!"任务".equals(text)) {
                        log.info("{}宝箱时间还没有到", pre);
                        continue;
                    }
                    导航_任务.click();
//                    this.closeAll();
                    // todo 任务睡觉/吃饭
                    this.checkTask();

                    ThreadUtil.sleep(1, TimeUnit.SECONDS);
                    kit.swipeUpSmall();
                    log.info("{}上滑", pre);
                    this.吃饭();
//                    this.睡觉();
                    kit.swipeDown();
                    log.info("{}下滑还原", pre);

                } else {
                    log.info("是否有点击过小视频按钮:{}", 导航_小视频.isSelected());
                    if (!导航_小视频.isSelected()) {
                        log.info("跳转到小视频");
                        导航_小视频.click();
                    }
                    this.look();
                }
            } catch (Exception e) {
                log.error("异常,跳出本循环", e);
                final SpringConfig springConfig = SpringUtil.getBean(SpringConfig.class);
                String dataPath = springConfig.getDataPath();
                String s = driver.startRecordingScreen();
                ThreadUtil.sleep(5, TimeUnit.SECONDS);
                String s1 = driver.stopRecordingScreen();
                String timeStr = DateUtil.formatDateTime(new Date());
                String folder = dataPath + File.separator + "error" + File.separator + "头条";
                String fileName = StrUtil.format("{}.mp4", timeStr);
                File file = Base64.decodeToFile(s1, new File(folder, fileName));
                log.error("异常保留截屏:{}", file.getAbsolutePath());

                break;
            } finally {
//                int i = RandomUtil.randomInt(1, 4);
//                log.info("间隔:{}", i);
//                ThreadUtil.sleep(i, TimeUnit.SECONDS);
            }
        }
    }

    private void 吃饭() {
        ToutiaoDay dayData = this.getDayData();
        //先判断这个时间是否已经吃过饭了
        List<DateTime> 吃饭签到s = dayData.get吃饭签到();
        if (吃饭签到s.stream().anyMatch(吃饭签到 -> 吃饭时间s.stream().anyMatch(t -> 吃饭签到.after(t.start) && 吃饭签到.before(t.end)))) {
            log.info("当前时间段已经有签到过了,吃饭签到:{}", 吃饭签到s);
            return;
        }
        DateTime now = DateTime.now();
        boolean is吃饭 = 吃饭时间s.stream().anyMatch(t -> now.after(t.start) && now.before(t.end));
        if (!is吃饭) {
            log.info("当前时间没有饭吃!!");
            return;
        }

        if (吃饭补贴.size() > 0) {
            log.info("找到吃饭按钮了..");
            MobileElement last = CollUtil.getLast(吃饭补贴);
            last.click();

            try {
                AndroidElement 领取吃饭金币 = (AndroidElement) driver.findElementByAndroidUIAutomator("""
                        new UiSelector().textMatches("领取.*金币")
                        """);
                领取吃饭金币.click();
                log.info("领取吃饭金币:{}", 领取吃饭金币.getText());
                dayData.get吃饭签到().add(new DateTime(DateUtil.format(new Date(), "HH:mm")));
                this.setDayData(dayData);
            } catch (Exception e) {

            }
            driver.navigate().back();
        }
    }

    private void 睡觉() {
        ToutiaoDay dayData = this.getDayData();
        if (睡觉赚钱 == null || 睡觉赚钱.size() == 0) {
            log.warn("睡觉按钮_没有找到");
            return;
        }
        int hour = DateUtil.hour(new Date(), true);
        if (hour >= 0 && hour < 2) {
            if (dayData.get开始睡觉()) {
                log.warn("已经睡过了");
                return;
            }
            log.info("找到睡觉按钮了..");
            MobileElement last = CollUtil.getLast(睡觉赚钱);
            last.click();
            try {
                AndroidElement 我要睡觉了 = (AndroidElement) driver.findElementByAndroidUIAutomator("""
                        new UiSelector().textMatches("我要睡了")
                        """);
                我要睡觉了.click();
                log.info("点击睡觉了:{}", 我要睡觉了.getText());
                dayData.set开始睡觉(true);
                this.setDayData(dayData);
            } catch (Exception e) {
                log.error("睡觉异常", e);
            }
        } else if (hour >= 9) {
            if (dayData.get睡醒了()) {
                log.warn("已经醒了");
                return;
            }
            log.info("找到睡觉按钮了..");
            MobileElement last = CollUtil.getLast(睡觉赚钱);
            last.click();
            //我醒了
            try {
                AndroidElement 我睡醒了 = (AndroidElement) driver.findElementByAndroidUIAutomator("""
                        new UiSelector().textMatches("我睡醒了")
                        """);
                AppiumUtils.Xy point = kit.getPoint(我睡醒了);
                kit.click(point);
                log.info("点击起床了:{}", 我睡醒了.getText());
                ThreadUtil.sleep(1, TimeUnit.SECONDS);
                log.info("领取睡后钱");
//                AndroidElement 睡醒领取金币 = (AndroidElement) driver.findElementByAndroidUIAutomator("""
//                        new UiSelector().textMatches("领取.*金币")
//                        """);
//                睡醒领取金币.click();
                kit.click(point);
                log.info("睡醒领取");
                ThreadUtil.sleep(1, TimeUnit.SECONDS);
                dayData.set睡醒了(true);
                this.setDayData(dayData);
            } catch (Exception e) {
                log.error("睡醒异常", e);
            }
        }
        driver.navigate().back();
    }


    public void closeAll() {
        TimeInterval timer = DateUtil.timer();
        log.info("检测弹窗耗时1:{}", timer.intervalRestart());
        AndroidElement 任务中每日签到弹窗 = (AndroidElement) driver.findElementByXPath(
                "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TabHost/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.webkit.WebView/android.webkit.WebView/android.view.View/android.view.View[1]/android.view.View[3]/android.view.View[2]/android.view.View[2]/android.view.View[11]/android.view.View[1]/android.view.View[2]");
        //todo 需要加入线程池
//        AndroidElement 任务中每日签到弹窗 = (MobileElement) driver.findElementByXPath(
//                "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TabHost/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.webkit.WebView/android.webkit.WebView/android.view.View/android.view.View[2]/android.view.View[2]/android.view.View/android.view.View/android.view.View[5]");

//        AndroidElement 每日提示看头条 = (MobileElement) driver.findElementByXPath(
//                "/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.TabHost/android.widget.FrameLayout[1]/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.webkit.WebView/android.webkit.WebView/android.view.View/android.view.View[2]/android.view.View[2]/android.view.View/android.view.View/android.widget.Image[3]");
        log.info("检测弹窗耗时2:{}", timer.intervalRestart());
        ThreadUtil.execAsync(() -> {
            this.close弹层("任务中每日签到弹窗", 任务中每日签到弹窗);
            return true;
        });
    }

    public boolean close弹层(String info, AndroidElement toClose) {
        try {
            kit.click(toClose, 0, 0);
            log.info("!!!!弹窗__{}", info);
            return true;
        } catch (Exception e) {
//            log.info("没有开心收下弹窗");
        }
        return false;
    }

    public void look() {
        try {
            kit.swipeUp();
            log.info("{}上滑", pre);
            ThreadUtil.sleep(500);

            if (RandomUtil.randomBoolean()) {
                log.info("{}点赞!!", pre);
                //不知道为什么找不到这个元素
                System.out.println("点赞 = " + 小视频_点赞);
                小视频_点赞.click();
            } else {
                log.info("{}不点赞", pre);
            }

            if (RandomUtil.randomBoolean()) {
                log.info("{}点击评论!!", pre);
                // Can't locate an element by this strategy
                if ("评论".equals(小视频_评论.getText())) {
                    log.warn("low逼,没有人评论,我也评论");
                    return;
                }
                小视频_评论.click();
                log.info("点击评论框:{}", 小视频_评论框);
                //        评论框.click();
                小视频_评论框.click();
                ThreadUtil.sleep(3, TimeUnit.SECONDS);
                String 评论 = RandomUtil.randomEle(PiCnf.评论s);
                log.info("输入评论:{}", 评论);
                kit.send("你好陌生人,{}", 评论);


                小视频_评论界面_发布.click();
                log.info("{},发布完成", pre);
                ThreadUtil.sleep(2, TimeUnit.SECONDS);
                driver.navigate().back();
                log.info("返回");
            } else {
                log.info("{}不评论", pre);
            }
        } catch (Exception e) {
            throw e;
        }
    }

    public void checkTask() {
        try {
            ThreadUtil.sleep(10, TimeUnit.SECONDS);
            log.info("{}进入任务界面", pre);
            //todo 点击宝箱
            log.info("{}点击宝箱", pre);
            kit.click(宝箱, 0, 0);
            ThreadUtil.sleep(10, TimeUnit.SECONDS);
            //todo 查看广告
            log.info("{}点击看视频", pre);
            看完视频再领金币.click();
            log.info("{}需要看25s视频", pre);
            ThreadUtil.sleep(25, TimeUnit.SECONDS);
            this.循环看广告();
        } catch (Exception e) {
            throw e;
        }
    }

    private void 循环看广告() {
        log.info("检查__循环看广告");
        try {
            while (true) {
                List<Boolean> flags = Lists.newArrayList();
                try {
                    // 需要循环看4次广告
                    List<AndroidElement> 再看广告 = (List<AndroidElement>) driver.findElementsByAndroidUIAutomator(
                            """
                                    new UiSelector().textMatches(".*再看一个获得.*金币")
                                    """);
                    if (再看广告 != null) {
                        log.info("再看广告");
                        AndroidElement last = CollUtil.getLast(再看广告);
                        last.click();
                        ThreadUtil.sleep(30, TimeUnit.SECONDS);
                        log.info("广告看完了..继续....");
                        flags.add(true);
                        continue;
                    }
                } catch (Exception e) {
                    log.warn("广告循环_没有再看广告");
                }

                try {
                    // 需要循环看4次广告
                    List<AndroidElement> 坚持退出 = (List<AndroidElement>) driver.findElementsByAndroidUIAutomator(
                            """
                                    new UiSelector().textMatches("坚持退出")
                                    """);
                    if (坚持退出 != null) {
                        log.info("坚持退出");
                        AndroidElement last = CollUtil.getLast(坚持退出);
                        last.click();
                        ThreadUtil.sleep(30, TimeUnit.SECONDS);
                        log.info("广告看完了..完全退出");
                        flags.add(true);
                        break;
                    }
                } catch (Exception e) {
                    log.warn("还没有到坚持退出");
                }

                try {
                    // 有开心收下表示最后一个广告
                    AndroidElement 关闭广告 = (AndroidElement) driver.findElementByAndroidUIAutomator(
                            """
                                    new UiSelector().textMatches("关闭")
                                    """);
                    if (关闭广告 != null) {
                        log.info("循环看广告_找到关闭按钮!!");
                        kit.click(关闭广告, 0, 0);
                        flags.add(true);
                        continue;
                    }
                } catch (Exception e) {
                    log.info("循环看广告_没有关闭按钮");
                }

                try {
                    // 有开心收下表示最后一个广告
                    AndroidElement 开心收下 = (AndroidElement) driver.findElementByAndroidUIAutomator(
                            """
                                    new UiSelector().textMatches("开心收下")
                                    """);
                    if (开心收下 != null) {
                        log.info("循环看广告_收下金币,所有广告已经看完!!");
                        kit.click(开心收下, 0, 0);
                        driver.navigate().back();
                        flags.add(true);
                        break;
                    }
                } catch (Exception e) {
                    log.info("循环看广告_没有收下金币");
                }

                if (flags.size()  == 0) {
                    log.info("循环__没有任务按钮点击.则没有广告,需要退出循环");
                    break;
                }
            }
        } catch (Exception e) {
            throw e;
        }
    }

    private void 发布微头条() {
        log.info("-----------微头条-----");
        log.info("点击导航");
        kit.click(导航_首页, 0, 0);
        log.info("点击_开始_发布按钮");
        kit.click(首页_发布, 0, 0);
        log.info("点击_微头条");
        kit.click(首页_发布_微头条, 0, 0);
        log.info("点击_微头条_文本框");
        kit.click(首页_发布_微头条_文本框, 0, 0);
        String 日记 = 日记s.remove(0);
        ThreadUtil.sleep(1, TimeUnit.SECONDS);

        log.info("---读取舔狗日记数:{},本次内容:{}", 日记s.size(), 日记);
        kit.send("你好陌生人,{}", 日记);
        log.info("点击_微头条_发布");
        kit.click(首页_发布_微头条_发布, 0, 0);
        cn.hutool.core.io.FileUtil.writeUtf8Lines(日记s, 舔狗日记);
        log.info("从舔狗日记删除:{}", 日记);
        hash.put(数据枚举.上次发布微头条.name(), new Date());
    }


    @Override
    public void openActivity() {
        driver.startActivity(mainActivity);
    }

    @Override
    public void checkAndInstallApk() {
        if (!driver.isAppInstalled(mainActivity.getAppPackage())) {
            log.info("{}没有安装正在安装中", pre);
            driver.installApp("/Users/chenwenxi/Downloads/apks/toutiao_jisu.apk");
        }
    }

    @Override
    public String appName() {
        return "头条极速版";
    }

    @Override
    public boolean isNeedRun() {
        return true;
    }

    @Data
    public static class ToutiaoDay implements Serializable {
        private Boolean 开始睡觉;
        private Boolean 睡醒了;
        private List<DateTime> 吃饭签到;
    }

    private ToutiaoDay getDayData() {
        String key = DateUtil.formatDate(new Date());
        return Optional.ofNullable(this.hashDay.get(key)).orElseGet(() -> {
            ToutiaoDay toutiaoDay = new ToutiaoDay();
            toutiaoDay.set吃饭签到(Lists.newArrayList());
            toutiaoDay.set开始睡觉(false);
            toutiaoDay.set睡醒了(false);
            this.hashDay.put(key, toutiaoDay);
            return toutiaoDay;
        });
    }

    private void setDayData(ToutiaoDay data) {
        String key = DateUtil.formatDate(new Date());
        log.info("持久化day,key:{},数据:{}", key, data);
        this.hashDay.put(key, data);
    }

    public enum 数据枚举 {
        上次发布微头条, 上次开宝箱
    }

    public record 吃饭时间(DateTime start, DateTime end) {
    }
}
